﻿function Column(options)
{
    this.options = {
        getName: function ()
        {
            return this.options.displayName || this.options.name;
        },
        getData: function (item)
        {
            return item[this.options.name];
        },
        getAttr: function (item)
        {
            return [];
        },
        getHiddenData: function (item)
        {
            return [];
        },
        width: null,
        className:""
        
    };

    this.getName = function ()
    {
        return this.options.getName.call(this);
    }

    this.getData = function (item)
    {
        return this.options.getData.call(this, item);
    }

    this.getAttr = function (item)
    {
        return this.options.getAttr.call(this, item);
    }

    this.getHiddenData = function (item)
    {
        return this.options.getHiddenData.call(this, item);
    }

    this.options = $.extend(this.options, options);
}

var ClarizenTable = {
    _init: function ()
    {
        this._getTotalWidth();
        this._drawLayout();
        this.drawHeader();
        this._drawData();
        this.page = 0;
        this.options.pageSize = this.options.pageSize || this.options.elements.length;


        var table = this;
        // if a paging function was given attach it to the scroll of the table
        if (this.options.page)
            this.dataContainer.scroll(function ()
            {
                if (table.PagingEnabled)
                    if (Math.abs(table.dataContainer[0].scrollHeight - table.dataContainer.scrollTop() - table.dataContainer.outerHeight()) < 10)
                    {// if the user has scrolled to the end of the table
                        if (table.isPaging) // do not call the function twice
                            return;
                        table.isPaging = true;
                        table.page++;
                        table._showPaging(true); // show the paging indicator
                        table.options.page(table.page, function (data) // call the paging function with a callback
                        {
                            table._addData(data); // add the elements to the table
                            table.isPaging = false;
                            table._showPaging(false); // hide paging indicator
                        });
                    }
            });
        // allow selection of cells with keyboard arrows
        $(document).keydown(function (e)
        {
            var inputFocused = false;
            $("input, textarea, select").each(function ()
            {
                if (document.activeElement)
                {
                    if (this == document.activeElement)
                        inputFocused = true;
                }
                else if ($(this).is(":focus"))
                    inputFocused = true;
            });

            if (inputFocused)
                return;

            if (e.keyCode > 36 && e.keyCode < 41)
            {
                e.preventDefault();
                table._moveSelection(e.keyCode);
            }

        });
    },

    dataTable: null,

    options: {
        cols: [],
        elements: null,
        width: null,
        height: 240,
        headerHeight: 49,
        baseUrl: "", // used for image loading (in gmail i need an absolute url)
        pageSize: 0,
        totalCount: 0, // total cound of elements being paged
        afterCreated: function (rows) { }, // event called after rows are created
        afterCellClick: function () { },
        alternateRowClass: "Odd",
        padding: 25,
        spanHeaderText: false,
        headerstyle: "Header"
    },

    _getTotalWidth: function ()
    {
        var width = 0;
        for (var i = 0; i < this.options.cols.length; i++)
            width += this.options.cols[i].options.width + (this.options.cols[i].options.padding > -1 ? this.options.cols[i].options.padding : this.options.padding);
        width += this.options.cols.length - 1;
        this.options.width = width;
    },
    _drawLayout: function ()
    {
        this.element.html(''); // clear the html in the container
        //this.element.width(this.options.width);
        //header of table
        this.header = $(stringFormat('<table class="HeaderTable" cellpadding="0" cellspacing="0">'));
        this.element.append(this.header);
        // scrolling container for data table
        this.dataContainer = $(stringFormat('<div class="DataTableContainer" style="height:{0}px"><table class="DataTable" cellpadding="0" cellspacing="0"></table></div>'
                                                    , this.options.height))
        this.element.append(this.dataContainer)
        this.dataTable = this.dataContainer.find('table');
    },
    // adds elements to list of elements and redraws the table
    _addData: function (data)
    {
        this._drawDataRows(data, this.options.elements.length);
        this.options.elements = this.options.elements.concat(data);
    },
    drawHeader: function ()
    {
        var table = this;
        this.header.html('');
        var tr = $(stringFormat('<tr class="{0}">', this.options.headerstyle));
        this.header.append(tr);
        for (var i = 0; i < this.options.cols.length; i++)
        {
            var text;
            if (this.options.spanHeaderText)
                text = stringFormat("<span>{0}</span>", this.options.cols[i].getName());
            else text = this.options.cols[i].getName();
            var th = $(stringFormat("<th col='{1}' style='padding-left:{2}px'>{0}</th>", text, i, this.options.cols[i].options.padding > -1 ? this.options.cols[i].options.padding : this.options.padding));
            tr.append(th);
        }
        var th = $("<th style='width:16px'></th>");
        tr.append(th);


        // attach click to header if given
        if (this.options.headerClick)
            tr.find('th').click(function ()
            {
                table.options.headerClick.call(this, arguments);
            });
    },
    // redraws the data table
    _drawData: function ()
    {
        if (!this.options.elements.length)
        {
            this._showNoData(true);
            return;
        }
        this._showNoData(false);

        var table = this;
        this.dataTable.html('');
        for (var i = 0; i < this.options.elements.length; i++)
        {
            var tr = this._getDataRow(this.options.elements[i], i);
            if (this.options.alternateRowClass && i % 2 == 1)
                tr.addClass(this.options.alternateRowClass);
            this.dataTable.append(tr);
        }

        this._attachCellEvents(this.dataTable.find('td'));
        this._adjustWidth();

        this.dataTable.show();
        // remove loading gif
        this.dataContainer.show_loading(false);
        // fire created event
        this.options.afterCreated(this.dataTable.find('tr'));
    },
    // adds rows to end of table
    _drawDataRows: function (elements, startRow)
    {
        var trs = $();
        for (var i = 0; i < elements.length; i++)
        {
            var tr = this._getDataRow(elements[i], i + startRow);
            this.dataTable.append(tr);

            if (this.options.alternateRowClass && (i + startRow) % 2 == 1)
                tr.addClass(this.options.alternateRowClass);

            trs = trs.add(tr); // TODO: find a better way
        }
        this._attachCellEvents(trs.find('td'));
        this._adjustWidth(trs);
        this.options.afterCreated(trs);
    },
    // get data row html
    _getDataRow: function (element, row)
    {
        var tr = $(stringFormat('<tr row="{0}">', row));
        tr.data('entity', element);

        for (var j = 0; j < this.options.cols.length; j++)
        {
            var col = this.options.cols[j];
            var td = $(stringFormat("<td col='{1}' class='{2}' style='padding-left:{3}px'>{0}</td>", col.getData(element), j, col.options.className ? col.options.className : "", col.options.padding > -1 ? col.options.padding : this.options.padding));
            tr.append(td);

            var attr = col.getAttr(element);
            for (var s = 0; s < attr.length; s++)
                td.attr(attr[s].name, attr[s].val);

            var data = this.options.cols[j].getHiddenData(element);
            for (var s1 = 0; s1 < data.length; s1++)
                td.data(data[s1].name, data[s1].val);
        }

        return tr;
    },
    // sets width of columns
    _adjustWidth: function (tr)
    {
        $.each(this.options.cols, function (idx, el)
        {
            if (el.options.width)
                $(stringFormat('[col={0}]', idx), tr).css({ "width": el.options.width + "px", "max-width": el.options.width + "px" });
        });
    },

    _attachCellEvents: function (cells)
    {
        var table = this;
        cells.click(function (e)
        {
            if (table.options.beforeCellClick)
                table.options.beforeCellClick.call(this, arguments);
            // select clicked cell
            table.selectCell($(this));
            e.stopPropagation();
            if (table.options.afterCellClick)
                table.options.afterCellClick.call(this, arguments);
        });
    },
    // redraw table with the new elements
    reload: function (elements, total, enablePaging)
    {
        this.page = 0;
        this.options.pageSize = this.options.pageSize || elements.length;
        this.options.elements = elements;
        this._drawData();
        if ($.type(enablePaging) == "boolean")
            this.enablePaging(enablePaging);
        // if total is a number update total count
        if (typeof total != 'undefined')
            this.options.totalCount = total;
    },

    showLoading: function ()
    {
        this.dataTable.hide();
        this.dataContainer.show_loading(true);
    },

    _showNoData: function (show)
    {
        if (show)
        {
            var div = $("<div class='coverDiv' style='width:100%; height:100%; background-color:white; position:absolute'>")
            this.dataContainer.append(div)
            div.append(stringFormat("<span class='' style='margin:{0}px {1}px;display:inline-block'>no data</span>", Math.ceil(div.height() / 2) - 12, div.width() / 2 - 25));
        }
        else
            this.dataContainer.find('.coverDiv').remove();
    },

    getSelectedRow: function ()
    {
        return this.dataTable.find('tr.Selected');
    },
    getSelectedCell: function ()
    {
        return this.dataTable.find('td.Selected');
    },
    getSelectedElement: function (element)
    {
        if (typeof (element) != 'undefined')
        {
            var tasks = this.getElements();

            var index = $.inObjectArray(tasks, function (el) { return el.ID.Value == element.ID.Value });
            if (index >= 0)
            {
                var row = this.dataTable.find(stringFormat("[row={0}]", index));
                row.data('entity', element);

                tasks[index] = element;
                $('#tasksContainer').clarizen_table('updateRow', index);
            }
        }
        else
            return this.getSelectedRow().data('entity');
    },
    getElements: function ()
    {
        return this.options.elements;
    },
    // add an element after a certain row
    addElement: function (element, after_tr)
    {
        var that = this;
        // get index of new row
        var iRow = +after_tr.attr('row') + 1;
        // add element to entity list 
        this.options.elements.splice(iRow, 0, element);
        var new_tr = this._getDataRow(element, iRow);
        if (this.options.alternateRowClass && iRow % 2 == 1)
            new_tr.addClass(this.options.alternateRowClass);
        // increment all row's indexes following the new row
        this.dataTable.find('tr').each(function ()
        {
            var tr = $(this);
            var ind = +tr.attr('row');
            if (ind >= iRow)
            {
                tr.attr('row', ind + 1);
                if (that.options.alternateRowClass)
                {
                    if ((ind + 1) % 2 == 1)
                        tr.addClass(that.options.alternateRowClass);
                    else
                        tr.removeClass(that.options.alternateRowClass);
                }
            }
        });
        // add new row
        after_tr.after(new_tr);
        this._attachCellEvents(new_tr.find('td'));
        this._adjustWidth(new_tr);
    }, // move selection using arrows
    _moveSelection: function (keyCode)
    {
        var cell = this.getSelectedCell();
        var row = this.getSelectedRow();
        if (!cell.length)
            this.selectCell(0, 0);
        var curRow = +row.attr('row');
        var curCol = +cell.attr('col');

        curCol = (curCol + (keyCode == 37 ? -1 : keyCode == 39 ? 1 : 0) + this.options.cols.length) % this.options.cols.length;
        curRow = (curRow + (keyCode == 38 ? -1 : keyCode == 40 ? 1 : 0) + this.options.elements.length) % this.options.elements.length;

        this.selectCell(curRow, curCol);
    },
    selectCell: function (row, col)
    {
        // row is either row or the cell
        var cell = row.length ? row : this.dataTable.find(stringFormat('[row={1}] [col={0}]', col, row));
        if (!cell.length)
            return;

        var tr = cell.closest('tr');
        if (!tr.hasClass('Selected'))
        {
            this.dataTable.find("tr").removeClass('Selected');
            tr.addClass('Selected');
        }
        if (!cell.hasClass('Selected'))
        {
            this.dataTable.find('td').removeClass('Selected');
            cell.addClass('Selected');
        }
        var top = tr.position().top;
        if (top < 0)
            this.dataContainer.scrollTop(this.dataContainer.scrollTop() + top)
        else if (top + tr.height() > this.dataContainer.outerHeight())
            this.dataContainer.scrollTop(this.dataContainer.scrollTop() + top + tr.height() - this.dataContainer.outerHeight());
    },
    enablePaging: function (enable)
    {
        this.PagingEnabled = enable;
        this.isPaging = false;
    },

    _showPaging: function (show)
    {
        if (!show)
            this.dataTable.find(".load_tr").remove();
        else
        {
            var tr = $("<tr class='load_tr'>");
            var td = $(stringFormat("<td colspan='{0}'>", this.options.cols.length));
            var img = stringFormat("<img src='{0}/Images/treeview/ajax-loader.gif' style='margin-right:7px;vertical-align:bottom'/>", this.options.baseUrl);
            var text = stringFormat("Retrieving next {0} {1}", this.options.pageSize, this.options.totalCount ? "out of " + this.options.totalCount : "");
            td.append(stringFormat("<span>{0}{1}</span>", img, text));
            tr.append(td);
            this.dataTable.append(tr);
        }
    },
    // get column object
    getCol: function (numORcell)
    {
        if (isNumber(numORcell))
            return this.options.cols[numORcell];
        else return this.options.cols[numORcell.attr('col')];
    },

    indicateSaving: function (show)
    {
        var con = this.header.find('[col=0]');

        if (show)
        {
            var ind = $('<div class="saveIndicator">');
            ind.css({ width: con.width(), height: con.height() - 2 })
            con.wrapInner("<div style='display:none'></div>");
            con.append(ind).css('position', 'relative');
        }
        else
        {
            $('.saveIndicator').remove();
            con.html(con.find('div').html())
        }
    },

    updateRow: function (index)
    {
        var row = this.dataTable.find(stringFormat("[row={0}]", index));
        var new_tr = this._getDataRow(row.data('entity'), index);
        row.replaceWith(new_tr);
        this._attachCellEvents(new_tr.find('td'));
        this._adjustWidth(new_tr);
    }
};

$.widget("ui.clarizen_table", ClarizenTable);